#ifndef FIELD_H
#define FIELD_H

#include <vector>


struct Vector3d
{
    Vector3d(int xVec = 1, int yVec = 1, int zVec = 1) : x(xVec), y(yVec), z(zVec) { }
    
    int x, y, z;
};

typedef Vector3d Size;
typedef Vector3d Position;


/// An "Object" or "Obstacle" in the map
class Obstacle
{
public:
    /// Constructor
    Obstacle(const Size &size = Size(1,1,1), const Position &pos = Position(0,0,0)) :
        m_size(size), m_pos(pos) {}
    
    void SetPosition(const Position &pos) { m_pos = pos; }
    Size GetPosition() const { return m_pos; }
            
    void SetSize(const Size &size) { m_size = size; }
    Size GetSize() const { return m_size; }
    
	int GetPosX() const { return m_pos.x; }
	int GetPosY() const { return m_pos.y; }
	int GetPosZ() const { return m_pos.z; }
    void GetPosX(int x) { m_pos.x = x; }
    void GetPosY(int y) { m_pos.y = y; }
    void GetPosZ(int z) { m_pos.z = z; }
        
	int GetSizeX() const { return m_size.x; }
	int GetSizeY() const { return m_size.y; }
	int GetSizeZ() const { return m_size.z; }
    void SetSizeX(int x) { m_size.x = x; }
    void SetSizeY(int y) { m_size.y = y; }
    void SetSizeZ(int z) { m_size.z = z; }
	
    
private:
	Size m_size;
//	Texture m_texture;
	Position m_pos;
};


/// A field, 
class Field
{
public:
    Field(const Size &limits = Size(10,10,10)) : m_limits(limits) {}
      
    Position MoveLimit(const Position &actualPos, const Position &nextPos, const Size &size)
    {
        Position newPos = nextPos;
        
        if (nextPos.x > m_limits.x)
            newPos.x = m_limits.x;
        if (nextPos.y > m_limits.y)
            newPos.y = m_limits.y;
        if (nextPos.z > m_limits.z)
            newPos.z = m_limits.z;

        for (int i = m_obstacles.size()-1; i >= 0; --i)
        {
            if (CheckCollision(m_obstacles[i], actualPos, nextPos, size))
            {
                newPos = CollisionPoint(m_obstacles[i], actualPos, nextPos, size);
                break;
            }
        }
        
        return newPos;
    }    
    
    
    /// Check if there is a collision within the the obstacule class ob and the trajectory p1<-->p2
    bool CheckCollision(const Obstacle &ob, const Position &pos1, const Position &pos2, const Size &size)
    {
        if ( (SimpleCollision(ob.GetPosX(), ob.GetSizeX() + ob.GetPosX(), pos1.x, pos2.x, size.x)) &&
         SimpleCollision(ob.GetPosY(), ob.GetSizeY() + ob.GetPosY(), pos1.y, pos2.y, size.y) &&
         SimpleCollision(ob.GetPosZ(), ob.GetSizeZ() + ob.GetPosZ(), pos1.z, pos2.z, size.z))
            return true;
        else
            return false;
    }

    /// Collision detection within a trajectory p1<-->p2, and an object with width, in 1 Dimension
    bool SimpleCollision(int obs1, int obs2, int pos1, int pos2, int size)
    {
        int a = pos1;
        int b = pos2 - size;
        int o = obs1;
        
        if (pos1 > (pos2 - size))
        {
            a = pos2;
            b = pos1;
            o = obs1;
        }
        
        if (a <= o && b >= o)
            return true;
        
        return false;
    }
    
    /// This function assumes that a collision exists
    Position CollisionPoint(const Obstacle &obs, const Position &pos1, const Position &pos2, const Size &size)
    {
        Position coll;
        coll.x = obs.GetPosX() - size.x;
        coll.y = obs.GetPosY() - size.y;
        coll.z = obs.GetPosZ() - size.z;
        
        if (pos1.x > pos2.x) 
            coll.x = obs.GetPosX() + obs.GetSizeX();
        if (pos1.y > pos2.y)
            coll.y = obs.GetPosY() + obs.GetSizeY();
        if (pos1.z > pos2.z) 
            coll.z = obs.GetPosZ() + obs.GetSizeZ();
        
        return coll;
    }
    
    Size GetLimits() { return m_limits; }
    void SetLimits(const Size &limits) { m_limits = limits; }

    void AddObstacle(const Obstacle &obs)
    {
        m_obstacles.push_back(obs);
    }
    
private:
    std::vector<Obstacle> m_obstacles;
    Size m_limits;   
};


#endif // FIELD_H
